package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

public class NIOServer {
    // 缓冲区容量大小
    private int bufferCapacity = 4096;
    // 发送缓冲区
    private ByteBuffer sendBuffer = ByteBuffer.allocate(bufferCapacity);
    // 接收缓冲区
    private ByteBuffer recBuffer = ByteBuffer.allocate(bufferCapacity);
    // 选择器
    private Selector selector;
    // 标识数据
    private AtomicInteger countSend = new AtomicInteger(0);

    public NIOServer() throws IOException {
        this(8888);
    }

    public NIOServer(int port) throws IOException {
        // 服务端Channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 绑定端口
        serverSocketChannel.bind(new InetSocketAddress(port));
        // 设置非阻塞
        serverSocketChannel.configureBlocking(false);
        // 打开选择器
        selector = Selector.open();
        // 将channel注册到选择器上，事件为：接收客户端连接
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Server start ->" + port);
    }

    /**
     * 监听事件
     */
    public void listen() throws IOException {
        while (true) {
            // 阻塞，直到有事件发生
            selector.select();
            // 获取selectionKey并循环
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                // 业务逻辑
                handleKey(selectionKey);
            }
        }
    }

    /**
     * 业务逻辑
     *
     * @param selectionKey
     */
    private void handleKey(SelectionKey selectionKey) throws IOException {
        if (selectionKey.isAcceptable()) { // 连接事件
            System.out.println(">>>>>>>>server OP_ACCEPT事件！");
            // 服务端ServerSocketChannel
            ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
            // accept到客户端SocketChannel
            SocketChannel socketChannel = server.accept();
            // 设置客户端为非阻塞
            socketChannel.configureBlocking(false);
            // 为客户端注册读事件
            socketChannel.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) { // 读事件
            System.out.println(">>>>>>>>server OP_READ事件！");
            // 客户端SocketChannel
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            // 读到缓冲区
            int len = socketChannel.read(recBuffer);
            // 读取到的数据
            if (len > 0) {
                // 为啥flip()??
                recBuffer.flip();
                String receiveText = new String(recBuffer.array(), 0, len);
                System.out.println("服务端接收到客户端的信息：" + receiveText);
                // 注册写事件
                SelectionKey sKey = socketChannel.register(selector, SelectionKey.OP_WRITE);
                // 读到的数据放到SelectionKey中
                sKey.attach(receiveText);
            }
        } else if (selectionKey.isWritable()) { // 写事件
            // 清理发送缓冲区
            sendBuffer.clear();
            System.out.println(">>>>>>>>server OP_WRITE事件！");
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            // 从key中拿到读取的数据作为输出
            String sendText = (String) selectionKey.attachment() + countSend.getAndIncrement();
            sendBuffer.put(sendText.getBytes());
            sendBuffer.flip();
            socketChannel.write(sendBuffer);
            System.out.println("服务端已经发送数据给客户端：" + sendText);
        }
    }

    public static void main(String[] args) throws IOException {
        NIOServer nioServer = new NIOServer();
        nioServer.listen();
    }
}
